<script setup>
import { ref, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import 'xterm/css/xterm.css';
import { ptySpawn, ptyResize, ptyWrite, ptyRead, ptyKill, listShells } from '../../tauriApi';
import { listen } from '@tauri-apps/api/event';
import { subscribe as subscribeTheme } from '../../assets/theme.js';
import { storeToRefs } from 'pinia';
import { useWorkspaceStore } from '../../stores/workspace';
import { useTerminalsStore } from '../../stores/terminals';

const containerRef = ref(null);
let term = null;
let fitAddon = null;
let ptyId = '';
let readerTimer = null;
let resizeObs = null;
const terminals = useTerminalsStore();
const shells = ref([]);
const showNewMenu = ref(false);
const newMenuRef = ref(null);
const { workspaceRoot } = storeToRefs(useWorkspaceStore());
let unlisten = null;
let themeObs = null;
let colorSchemeMql = null;
let themeCheckTimer = null;
let lastDark = false;
let themeUnsub = null;
// Add a retry timer for safe fitting when container isn't ready
let fitRetryTimer = null;

function isDark() { try { return document.documentElement.classList.contains('dark'); } catch { return false; } }
function getXtermTheme() {
  const dark = isDark();
  return dark
    ? {
        // Dark: black background with white text
        background: 'transparent',
        foreground: '#ffffff',
        cursor: '#ffffff',
        selectionBackground: '#ffffff33',
      }
    : {
        // Light: black text per request
        background: 'transparent',
        foreground: '#000000',
        cursor: '#000000',
        selectionBackground: '#93c5fd66',
      };
}
function applyXtermTheme() {
  if (!term) return;
  const theme = getXtermTheme();
  try { term.setOption('theme', theme); } catch {}
  // force canvas refresh
  try { term.refresh(0, term.rows - 1); } catch {}
}

async function recreateTerminal() {
  try { term && term.dispose(); } catch {}
  const theme = getXtermTheme();
  term = new Terminal({
    convertEol: true,
    cursorBlink: true,
    allowTransparency: true,
    fontSize: 12,
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, \'Liberation Mono\', monospace',
    theme,
  });
  term.open(containerRef.value);
  applyXtermTheme();
  fit();
  try { term.offData && term.offData(handleData); } catch {}
  try { term.offKey && term.offKey(onKey); } catch {}
  term.onData(handleData);
  term.onKey(onKey);
  term.onResize(({ cols, rows }) => { if (ptyId) ptyResize(ptyId, cols, rows).catch(()=>{}); });
  setTimeout(() => fitAndResize(), 0);
}

function handleData(data) {
  // raw pass-through to PTY
  if (!ptyId) return;
  ptyWrite(ptyId, data).catch(()=>{});
}

function onKey({ key, domEvent }) {
  if (!ptyId) return;
  if (domEvent.ctrlKey && domEvent.key.toLowerCase() === 'l') { term.clear(); domEvent.preventDefault(); }
}

// Replace naive fit with safe fit that waits for visible, sized container
function fit() {
  try {
    if (!fitAddon || !containerRef.value) return;
    const el = containerRef.value;
    let rect = null;
    try { rect = el.getBoundingClientRect ? el.getBoundingClientRect() : null; } catch {}
    const width = rect ? rect.width : (el.clientWidth || 0);
    const height = rect ? rect.height : (el.clientHeight || 0);
    const visible = width > 0 && height > 0 && document.body.contains(el);
    if (!visible) {
      if (fitRetryTimer) { clearTimeout(fitRetryTimer); }
      fitRetryTimer = setTimeout(() => { fitRetryTimer = null; fit(); }, 50);
      return;
    }
    fitAddon.fit();
    // If rows/cols are still zero, retry shortly
    if (term && (term.rows === 0 || term.cols === 0)) {
      if (fitRetryTimer) { clearTimeout(fitRetryTimer); }
      fitRetryTimer = setTimeout(() => { fitRetryTimer = null; fit(); }, 50);
    }
  } catch {}
}
function fitAndResize() {
  try {
    fit();
    if (ptyId && term && term.cols && term.rows) { ptyResize(ptyId, term.cols, term.rows).catch(()=>{}); }
  } catch {}
}

onMounted(async () => {
  // Set theme at creation time to avoid blank canvas
  term = new Terminal({
    convertEol: true,
    cursorBlink: true,
    allowTransparency: true,
    fontSize: 12,
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, \'Liberation Mono\', monospace',
    theme: getXtermTheme(),
  });
  fitAddon = new FitAddon();
  term.loadAddon(fitAddon);
  term.open(containerRef.value);
  fit();
  applyXtermTheme();
  try { term.refresh(0, term.rows - 1); } catch {}
  term.onData(handleData);
  term.onKey(onKey);
  window.addEventListener('resize', fitAndResize);
  // observe container resize (bottom panel drag)
  try {
    resizeObs = new ResizeObserver(() => { fitAndResize(); });
    if (containerRef.value) resizeObs.observe(containerRef.value);
  } catch {}
  try { shells.value = await listShells(); } catch {}
  await attachToActive();
  // event stream subscription is managed in attachToActive()
  // observe theme changes via class mutation on <html>
  try {
    themeObs = new MutationObserver(() => applyXtermTheme());
    themeObs.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
  } catch {}
  // media query fallback
  try {
    colorSchemeMql = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
    if (colorSchemeMql && colorSchemeMql.addEventListener) {
      colorSchemeMql.addEventListener('change', () => recreateTerminal());
    }
  } catch {}
  // subscribe to app theme store (authoritative)
  try { themeUnsub = subscribeTheme(() => recreateTerminal()); } catch {}
  // periodic fallback in case theme toggles by other means
  lastDark = isDark();
  themeCheckTimer = window.setInterval(async () => {
    const cur = isDark();
    if (cur !== lastDark) { lastDark = cur; await recreateTerminal(); }
  }, 500);
});

onBeforeUnmount(async () => {
  try { window.removeEventListener('resize', fitAndResize); } catch {}
  try { if (resizeObs) { resizeObs.disconnect(); resizeObs = null; } } catch {}
  try { if (readerTimer) { clearTimeout(readerTimer); readerTimer = null; } } catch {}
  try { if (ptyId) await ptyKill(ptyId); } catch {}
  try { term && term.dispose(); } catch {}
  try { if (unlisten) { unlisten(); unlisten = null; } } catch {}
  try { if (themeObs) { themeObs.disconnect(); themeObs = null; } } catch {}
  try { if (colorSchemeMql && colorSchemeMql.removeEventListener) { colorSchemeMql.removeEventListener('change', () => applyXtermTheme()); } } catch {}
  try { if (themeCheckTimer) { clearInterval(themeCheckTimer); themeCheckTimer = null; } } catch {}
  try { if (themeUnsub) { themeUnsub(); themeUnsub = null; } } catch {}
  try { if (fitRetryTimer) { clearTimeout(fitRetryTimer); fitRetryTimer = null; } } catch {}
});

async function attachToActive() {
  try {
    if (readerTimer) { clearTimeout(readerTimer); readerTimer = null; }
    if (ptyId) { try { await ptyKill(ptyId); } catch {} ptyId=''; }
    const spec = terminals.items.find(t => t.id === terminals.activeId) || { name: 'Terminal', program: undefined, args: undefined, cwd: String(workspaceRoot.value || '') };
    const cols = term.cols || 80; const rows = term.rows || 24;
    const [id] = await ptySpawn(spec.cwd || undefined, cols, rows, spec.program, spec.args);
    ptyId = id;
    term.onResize(({ cols, rows }) => { ptyResize(ptyId, cols, rows).catch(()=>{}); });
    // re-bind event listener for new id
    try { if (unlisten) { unlisten(); unlisten = null; } } catch {}
    const topic = `pty:data:${ptyId}`;
    unlisten = await listen(topic, (e) => {
      const chunk = typeof e.payload === 'string' ? e.payload : '';
      if (chunk) term.write(chunk.replace(/\r?\n/g, '\r\n'));
    });
    setTimeout(() => fitAndResize(), 50);
  } catch (e) {
    term.write(`\r\nPTY attach failed: ${String(e)}`);
  }
}

watch(() => terminals.activeId, async () => { await attachToActive(); });

function selectTerminal(id) { terminals.activate(id); }
function newFromShell(spec) { const cwd = String(workspaceRoot.value || ''); const t = terminals.create({ name: spec.name, program: spec.program, args: spec.args || [], cwd }); terminals.activate(t.id); showNewMenu.value=false; }
function newDefault() { const cwd = String(workspaceRoot.value || ''); const t = terminals.create({ name: 'Terminal', cwd }); terminals.activate(t.id); showNewMenu.value=false; }
async function kill(id) { if (terminals.activeId === id && ptyId) { try { await ptyKill(ptyId); } catch {} ptyId=''; } terminals.remove(id); }
function toggleNewMenu() { showNewMenu.value = !showNewMenu.value; }
</script>

<template>
  <div class="flex h-full min-h-0">
    <div ref="containerRef" class="flex-1 min-h-0 w-full"></div>
    <aside class="w-44 border-l border-panel-border p-1 text-[12px] flex flex-col">
      <div class="flex items-center justify-between px-1 py-1">
        <div class="font-semibold">Terminals</div>
        <div class="relative" ref="newMenuRef">
          <button class="h-6 w-6 rounded hover:bg-app-bg" @click="showNewMenu=!showNewMenu">+</button>
          <div v-show="showNewMenu" class="absolute right-0 top-full mt-1 min-w-[200px] z-50 bg-panel-bg border border-panel-border rounded shadow p-1">
            <button class="block w-full text-left px-2 py-1 rounded hover:bg-app-bg" @click="newDefault">New Terminal</button>
            <button v-for="s in shells" :key="s.id" class="block w-full text-left px-2 py-1 rounded hover:bg-app-bg" @click="newFromShell(s)">{{ s.name }}</button>
          </div>
        </div>
      </div>
      <div class="flex-1 min-h-0 overflow-auto">
        <div v-for="t in terminals.items" :key="t.id" class="px-2 py-1 rounded cursor-pointer flex items-center justify-between hover:bg-app-bg" :class="t.id===terminals.activeId ? 'bg-app-bg' : ''" @click="selectTerminal(t.id)">
          <span class="truncate">{{ t.name }}</span>
          <button class="h-5 w-5 rounded hover:bg-app-bg/70" title="Kill" @click.stop="kill(t.id)">×</button>
        </div>
      </div>
    </aside>
  </div>
</template>

<style>
/* Xterm scrollbar theming */
.xterm .xterm-viewport { scrollbar-width: thin; scrollbar-color: rgba(125,125,125,0.4) transparent; }
.xterm .xterm-viewport::-webkit-scrollbar { width: 8px; height: 8px; }
.xterm .xterm-viewport::-webkit-scrollbar-track { background: transparent; }
.xterm .xterm-viewport::-webkit-scrollbar-thumb { background: rgba(125,125,125,0.4); border-radius: 6px; }
.xterm .xterm-viewport::-webkit-scrollbar-thumb:hover { background: rgba(125,125,125,0.55); }
/* Improve selection visibility */
.xterm .xterm-selection div { background-color: rgba(100, 149, 237, 0.35) !important; }
/* Force background to follow container for both light/dark */
.xterm, .xterm .xterm-screen, .xterm .xterm-viewport, .xterm .xterm-rows {
  background-color: transparent !important;
}
</style>


